package utilities;

import engine.Candidate;
import engine.Equation;
import java.util.ArrayList;
import matlabcontrol.MatlabConnectionException;
import matlabcontrol.MatlabInvocationException;
import matlabcontrol.MatlabProxy;
import matlabcontrol.MatlabProxyFactory;
import matlabcontrol.MatlabProxyFactoryOptions;

public class MatLabCOM implements Runnable {

    private MatlabProxyFactory factory;
    private MatlabProxy proxy;
    private Boolean isConnectedToMatLab;
    private String location;

    public MatLabCOM() {
        this.isConnectedToMatLab = false;
    }

    public Boolean getIsConnectedToMatLab() {
        return isConnectedToMatLab;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    /*
     * Une connection doit etre établie pour effectuer des calculs avec matlab
     */
    public void connect() {
        MatlabProxyFactoryOptions options = new MatlabProxyFactoryOptions.Builder()
                .setUsePreviouslyControlledSession(true)
                .setMatlabLocation(this.location)
                .build();


        factory = new MatlabProxyFactory(options);

        try {
            proxy = factory.getProxy();
            this.isConnectedToMatLab = true;
            System.out.println("MatLab Connexion succeeded.");
        } catch (MatlabConnectionException ex) {
            //TODO Exception
            System.out.println("Exception : MatLab Connexion failed. ( " + ex.getMessage() + " )");
        }
    }

    /*
     * Déconnection lors de la fermeture du programme
     */
    public void disconnect() {
        if (this.isConnectedToMatLab) {
            proxy.disconnect();
        }
    }

    /*
     * Retourne le résultat du candidat c injecté dans l'équation eq
     * Exemple : 
     *  - Equation : 2*x1+sin(x2)/exp(x2*55)-cos(x3^7)
     *  - Candidat : {1.0, 75.5, 89.1}
     */
    public Double evaluate(Equation eq, Candidate c) {
        Double result = null;

        try {
            proxy.eval("x = eval('" + injectCandidateInEquation(eq, c) + "')");
            result = ((double[]) proxy.getVariable("x"))[0];
        } catch (MatlabInvocationException ex) {
            //TODO Exception
            System.out.println("Exception : MatLab Communication failed. ( " + ex.getMessage() + " )");
        }

        return result;
    }

    /*
     * Retourne faux si le caractère c ne peut etre casté en Integer
     */
    private Boolean parseInt(char c) {
        boolean succeed = true;

        try {
            Integer.parseInt(Character.toString(c));
        } catch (NumberFormatException ex) {
            succeed = false;
        }

        return succeed;
    }

    /*
     * La connection à MatLab doit être asynchrone afin de ne pas geler l'interface graphique
     */
    @Override
    public void run() {
        connect();
    }

    class Variable {

        public String fullName;
        public Integer posStart;
        public Integer posEnd;
        public Integer val;

        public Variable() {
        }
    }

    /*
     * Retourne la chaine de caractère de l'équation remplacé par les valeurs du candidat
     * Exemple : 
     *  - Equation : 2*x1+sin(x2)/exp(x2*55)-cos(x3^7)
     *  - Candidat : {1.0, 75.5, 89.1}
     *  - Return   : 2*1.0+sin(75.5)/exp(75.5*55)-cos(89.1^7)
     */
    public String injectCandidateInEquation(Equation eq, Candidate cand) {
        String result = "";

        ArrayList<Variable> vars = new ArrayList<Variable>();

        //On cherche les position de fin et de début des variables
        char c, c2;
        boolean enAttenteDeFin;
        Variable curVar;

        for (int i = 0; i < eq.getEquation().length(); i++) {
            c = eq.getEquation().charAt(i);

            if (c == eq.getVarName().charAt(0)) {//varName trouvé
                curVar = new Variable();
                curVar.posStart = i;
                vars.add(curVar);
                enAttenteDeFin = true;

                //Recherche de la fin de la variable
                for (int j = i + 1; j < eq.getEquation().length(); j++) {
                    c2 = eq.getEquation().charAt(j);

                    //parse échoue, on a trouvé la fin
                    if (!parseInt(c2)) {
                        curVar.posEnd = j;
                        enAttenteDeFin = false;
                        break;//sortie de boucle
                    }
                }

                //Si dernière caractère est tjs parsable, c'est que c'est la fin de la variable
                if (enAttenteDeFin) {
                    curVar.posEnd = eq.getEquation().length();
                }

                //positions de la variable sont déterminés, on détermine le nombre de la variable et le nom complet
                curVar.fullName = eq.getEquation().substring(curVar.posStart, curVar.posEnd);
                curVar.val = Integer.parseInt(eq.getEquation().substring(curVar.posStart + 1, curVar.posEnd));
            }
        }

        //Remplace les variables par les valeurs
        String currentVectorValue;
        result = eq.getEquation();

        for (int i = 0; i < vars.size(); i++) {
            //Si le nombre de bornes est inférieur au nombre de variables
            //TO DO Gestion propre ?
            if(cand.getValues().size() <= (vars.get(i).val - 1)){
                return "";
            }
            
            currentVectorValue = cand.getValues().get(vars.get(i).val - 1).toString();

            result = result.replaceAll(vars.get(i).fullName, currentVectorValue);

            //System.out.println(vars.get(i).fullName + " ; " + currentVectorValue);
        }

        //System.out.println("eq=" + result);
        return result;
    }
}
